package com.kangjia.resolver;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.reflect.MethodUtils;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.core.MethodParameter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.accept.ContentNegotiationManager;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor;

import com.kangjia.annotation.JsonRequestBody;
import com.kangjia.holder.ContextHolder;

public class ValidatorRequestResponseBodyMethodProcessor extends RequestResponseBodyMethodProcessor {

	private Map<String, Object> params = new HashMap<String, Object>();

	public ValidatorRequestResponseBodyMethodProcessor(List<HttpMessageConverter<?>> converters,
			ContentNegotiationManager manager, List<Object> requestResponseBodyAdvice) {
		super(converters, manager, requestResponseBodyAdvice);
	}

	public ValidatorRequestResponseBodyMethodProcessor(List<HttpMessageConverter<?>> converters,
			ContentNegotiationManager manager) {
		super(converters, manager);
	}

	public ValidatorRequestResponseBodyMethodProcessor(List<HttpMessageConverter<?>> converters,
			List<Object> requestResponseBodyAdvice) {
		super(converters, requestResponseBodyAdvice);
	}

	public ValidatorRequestResponseBodyMethodProcessor(List<HttpMessageConverter<?>> converters) {
		super(converters);
	}

	public boolean supportsParameter(MethodParameter parameter) {
		return parameter.hasParameterAnnotation(JsonRequestBody.class);
	}

	protected <T> Object readWithMessageConverters(NativeWebRequest webRequest, MethodParameter parameter,
			Type paramType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {
		HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
		ServletServerHttpRequest inputMessage = new ServletServerHttpRequest(servletRequest);
		Object arg = readWithMessageConverters(inputMessage, parameter, paramType);
		if (arg == null) {
			if (checkRequired(parameter)) {
				throw new HttpMessageNotReadableException(
						"Required request body is missing: " + parameter.getMethod().toGenericString());
			}
		}
		JsonRequestBody jsonRequestBody = parameter.getParameterAnnotation(JsonRequestBody.class);
		boolean validator = jsonRequestBody.validator();
		if (validator) {
			Class<?> clazz = jsonRequestBody.clazz();
			String cmethod = jsonRequestBody.cmethod();
			String mkey = clazz.getName() + "." + cmethod;
			Object object = params.get(mkey);
			if (object == null) {
				object = BeanFactoryUtils.beanOfType(ContextHolder.getContext(), clazz);
				if (object != null) {
					params.put(mkey, object);
				} else {
					throw new RuntimeException("not find clazz=" + clazz.getName());
				}
			}
			try {
				Boolean is = (Boolean) MethodUtils.invokeMethod(object, cmethod, arg);
				if (!is) {
					throw new RuntimeException(parameter.getMethod().toGenericString() + " validator fail");
				}
			} catch (NoSuchMethodException e) {
				throw new RuntimeException("NoSuchMethodException", e);
			} catch (IllegalAccessException e) {
				throw new RuntimeException("IllegalAccessException", e);
			} catch (InvocationTargetException e) {
				throw new RuntimeException("InvocationTargetException", e);
			}
		}
		return arg;
	}

}
